const {
  add
} = require("lodash");

/**
 * 因为promise的状态固定只有3个，所以将这3个状态定义为常量
 */
const PENDING = "pending"; //等待
const FULFILLED = "fulfilled"; //成功
const REJECTED = "rejected"; //失败

class MyPromise {
  /**
   * 
   * @param {*} excutor ：构建promise对象时传递的立即执行的构造器，这个构造器有两个参数，分别是resolve和reject
   */
  constructor(excutor) {
    executor(this.resolve, this.reject);
  }

  status = PENDING; //定义promise对象的初始状态为pending状态
  value = undefined; //用于保存成功时的值
  resson = undefined; //用于保存失败时的值
  successCallback = []; //用于保存成功回调，之所以定义成数组，是因为then支持链式和多次调用
  failCallback = []; //用于保存失败回调，原因通成功回调数组

  /**
   * resolve方法用于将promise对象的状态有pending=>fulfilled，
   * 定义成箭头函数是因为我们在创建promise对象的执行器中是直接调用的，并没有指定this
   * @param {*} value ：成功时的值
   */
  resolve = value => {
    /**
     * 这里加上状态的判断，是为了阻止当promise对象的状态已经变成fulfilled或者rejected时，状态再次被改变
     */
    if (this.status !== PENDING) return;
    this.status = FULFILLED; //将状态置为fulfilled
    this.value = value; //保存成功时的值
    while (this.successCallback.length) {
      this.successCallback.shift()(); //依次调用成功回调里面的函数，并弹出
    }
  }

  reject = reason => {
    if (this.status !== PENDING) return;
    this.status = REJECTED;
    this.reason = reason;
    while (this.failCallback.length) {
      this.failCallback.shift()();
    }
  }

  then(successCallback, failCallback) {
    /**
     * 当then方法中没有指定成功回调时，我们给一个默认的方法，直接将值传递下去
     */
    successCallback = successCallback ? successCallback : value => value;
    /**
     * 当then方法中没有指定成功回调时，我们给一个默认的方法，将失败原因抛到下一个then方法中
     */
    failCallback = failCallback ? failCallback : reason => {
      throw reason;
    }
    /**
     * 在then方法中，需要返回一个新的promise对象，因为then方法可以链式调用
     */
    let promise2 = new MyPromise((resolve, reject) => {
      if (this.status === FULFILLED) { //如果状态是成功，则执行成功回调
        //这里调用了setTimeout来延迟代码执行，是因为下面的代码中引用到了promise2，
        //如果不异步执行的话，应用promise2时promise2还没有创建，会报错
        setTimeout(() => {
          try {
            let x = successCallback(this.value);
            /**
             * 这里定义的resolvePromise的作用有
             * 1、判断promise2是否和成功回调返回的是同一个，如果是同一个的话，需要抛出异常
             * 2、判断x是否是promise对象，如果是promise对象，则需要调用x的then方法，如果不是，则直接将resolve(x)返回
             */
            resolvePromise(promise2, x, resolve, reject);
          } catch (e) {
            reject(e);
          }
        }, 0)
      } else if (this.status === REJECTED) {
        setTimeout(() => {
          try {
            let x = failCallback(this.reason);
            resolvePromise(promise2, x, resolve, reject);
          } catch (e) {
            reject(e);
          }
        }, 0)
      } else { //如果还是pending状态，则将成功和失败回调进行保存
        this.successCallback.push(() => {
          setTimeout(() => {
            try {
              let x = successCallback(this.value);
              resolvePromise(promise2, x, resolve, reject);
            } catch (e) {
              reject(e);
            }
          }, 0)
        })
        this.failCallback.push(() => {
          setTimeout(() => {
            try {
              let x = failCallback(this.reason);
              resolvePromise(promise2, x, resolve, reject);
            } catch (e) {
              reject(e);
            }
          }, 0)
        })
      }
    })
    return promise2;
  }

  /**
   * 
   * @param {*} callback：回调函数
   * finally方法不管promise对象是成功还是失败，都会调用其回调函数 
   */
  finally(callback) {
    return this.then(value => {
      return MyPromise.resolve(callback()).then(() => value);
    }, reason => {
      return MyPromise.resolve(callback()).then(() => {
        throw reason;
      });
    })
  }
  /**
   * 
   * @param {*} failCallback :失败回调
   * 这个方法就是then方法没有定义成功回调的情况
   */
  catch (failCallback) {
    return this.then(undefined, failCallback);
  }

  /**
   * 
   * @param {*} array ：一个数组，数组中可以是普通值，也可以是promise对象
   * 最终返回的结果会以传入参数的顺序一致，
   * 由于all方法返回后还可以调用then方法，所以then方法返回的是一个promise对象
   */
  static all(array) {
    let result = []; //保存最后的结果
    let index = 0; //计数器
    return new MyPromise((resolve, reject) => {
      /**
       * 这个方法用于将入参返回的值放到对应的位置上，
       */
      function addData(key, value) {
        result[key] = value;
        index++;
        if (index === array.length) {
          resolve(result);
        }
      }

      for (leti = 0; i < array.length; i++) {
        let current = array[i];
        if (current instanceof MyPromise) { //如果是promise对象，则要传入then方法成功回调处理后的值
          current.then(value => addData(i, value), reason => reject(reason));
        } else {
          addData(i, value); //如果是普通值，则直接放进去
        }
      }
    })
  }

  /**
   * 
   * @param {*} value 
   * 改变promise对象的状态，并将value传递下去
   * 如果value是一个promise对象，则直接把这个promise对象传递下去
   * 如果value是一个普通值，则要将这个普通值构造成一个状态成功且值为value的promise对象
   */
  static resolve(value) {
    if (value instanceof MyPromise) {
      return value;
    } else {
      return new MyPromise(resolve => {
        resolve(value);
      })
    }
  }
}

function resolvePromise(promise, x, resolve, reject) {
  if (promise === x) {
    return reject(new TypeError('type error...'));
  }
  if (x instanceof MyPromise) {
    x.then(resolve, reject);
  } else {
    resolve(x);
  }
}